﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using ZeroconfService;
using Remoter;
using System.IO.Ports;
using System.IO;

namespace LampService
{
    partial class LampService : ServiceBase
    {
        bool mPublishing = false;
        NetService publishService = null;
        private Socket m_socket = null;
        private const int BUFFER_SIZE = 1024;
        public byte[] m_buffer = new byte[BUFFER_SIZE];
        byte[] data;
        int port = 8308;
        private SerialPort serialPort = new SerialPort();
        Remoter.Remoter remoter;

        public LampService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            //publish Bonjour service
            try
            {
                publishService = new NetService("", "_LampService._tcp.", "Lamp Service", this.port);

                System.Collections.Hashtable dict = new System.Collections.Hashtable();
                byte[] msg = Encoding.UTF8.GetBytes("You have connected Lamp Service.");
                dict.Add("Welcome", msg);

                publishService.TXTRecordData = NetService.DataFromTXTRecordDictionary(dict);

                publishService.Publish();
                this.eventLog.WriteEntry("published! -- " + DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"));
                mPublishing = true;
            }
            catch { }

            //init socket and listening to port
            m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            IPEndPoint localEP = new IPEndPoint(IPAddress.Any, this.port);
            m_socket.Bind(localEP);
            IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint epSender = (EndPoint)ipeSender;
            data = new byte[BUFFER_SIZE];
            m_socket.BeginReceiveFrom(data, 0, data.Length, SocketFlags.None, ref epSender, new AsyncCallback(OnReceive), epSender);
            this.eventLog.WriteEntry("listening to " + this.port + " port... -- " + DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"));
        }

        private void OnReceive(IAsyncResult ar)
        {
            try
            {
                IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
                EndPoint epSender = (EndPoint)ipeSender;
                m_socket.EndReceiveFrom(ar, ref epSender);

                SocketCommand cmd = new SocketCommand(data);
                SocketCommand cmdToSend = new SocketCommand();
                switch (cmd.Type)
                {
                    case CommandType.GetUSB:
                        this.eventLog.WriteEntry("A coming user ask for GetUSB(). -- " + DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"));
                        string[] comPorts = System.IO.Ports.SerialPort.GetPortNames();
                        cmdToSend.Type = CommandType.GetUSB;
                        cmdToSend.Message = string.Join(";", comPorts);
                        m_socket.BeginSendTo(cmdToSend.ToByte(), 0, cmdToSend.ToByte().Length, SocketFlags.None, epSender,
                                new AsyncCallback(OnSend), epSender);
                        break;
                    case CommandType.Switcher:
                        this.eventLog.WriteEntry("A coming user ask for Switcher(). -- " + DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"));
                        string portName = cmd.Message.Split(':')[0];
                        string cmdText = cmd.Message.Split(':')[1];
                        if (!this.serialPort.IsOpen)
                            this.OpenPort(portName);

                        remoter = new Remoter.Remoter(new Command(this.serialPort, cmdText));
                        string result = remoter.Execute();
                        cmdToSend.Type = CommandType.Switcher;
                        cmdToSend.Message = result;
                        m_socket.BeginSendTo(cmdToSend.ToByte(), 0, cmdToSend.ToByte().Length, SocketFlags.None, epSender,
                                new AsyncCallback(OnSend), epSender);
                        break;
                    case CommandType.Arduino:
                        break;
                    case CommandType.Null:
                        break;
                    default:
                        break;
                }

                m_socket.BeginReceiveFrom(data, 0, data.Length, SocketFlags.None, ref epSender, new AsyncCallback(this.OnReceive), epSender);
            }
            catch (Exception ex)
            {
            }
        }

        public void OnSend(IAsyncResult ar)
        {
            try
            {
                m_socket.EndSend(ar);
            }
            catch (Exception ex)
            {
            }
        }

        private bool OpenPort(string portName)
        {
            bool error = false;

            // If the port is open, close it.
            if (serialPort.IsOpen)
                serialPort.Close();

            // Set the port's settings
            serialPort.BaudRate = 9600;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;
            serialPort.Parity = Parity.None;
            serialPort.PortName = portName;

            try
            {
                // Open the port
                serialPort.Open();
            }
            catch (UnauthorizedAccessException) { error = true; }
            catch (IOException) { error = true; }
            catch (ArgumentException) { error = true; }

            return !error;
        }

        protected override void OnStop()
        {
            if(mPublishing)
                publishService.Stop();
            this.eventLog.WriteEntry("Lamp Service has stoped. -- " + DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"));
        }
    }
}
